﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Genuilder.Extensibility
{
	public class GenItemCollection : GenItemCollectionBase, IEnumerable<GenItem>
	{
		GenItem _Owner;

		public GenItemCollection(GenItem owner)
		{
			_Owner = owner;
		}

		protected override GenItemRepository Repository
		{
			get
			{
				return _Owner.Repository;
			}
		}

		

		List<GenItem> _Children = new List<GenItem>();

		internal void AddChildren(IEnumerable<GenItem> children)
		{
			foreach(var child in children)
			{
				Add(child);
			}
		}

		internal void Add(GenItem child)
		{
			child.Parent = _Owner;
			_Children.Add(child);
			Repository.Register(child);
		}


		internal void Remove(GenItem child)
		{
			if(_Children.Remove(child))
			{
				child.Parent = null;
				Repository.UnRegister(child);
			}
		}
		protected override ExecutionContext ExecutionContext
		{
			get
			{
				return _Owner.Repository.GetExecutionContext();
			}
		}

		protected override GenItem Owner
		{
			get
			{
				return _Owner;
			}
		}

		internal void ChangeParent(GenItem newParent)
		{
			if(Owner.Parent != null)
				Owner.Parent.Children._Children.Remove(Owner);
			if(newParent != null)
				newParent.Children._Children.Add(Owner);
			Owner.Parent = newParent;
		}

		#region IEnumerable<GenItem> Members

		public override IEnumerator<GenItem> GetEnumerator()
		{
			return _Children.GetEnumerator();
		}

		#endregion
		
	}

	public class GenItem
	{
		GenEngineStartInfo _StartInfo;
		internal GenItem(SourceFile sourceFile, GenEngineStartInfo startInfo, IGenLogger logger, bool generated)
		{
			File = new FileInfo(sourceFile.Name);
			_StartInfo = startInfo;
			_Logger = logger.CreateGenItemLogger(this);
			_GlobalLogger = logger;
			_SourceFile = sourceFile;
			_Extensions = new ExtensionRepository(this);
			_Generated = generated;
			_Children = new GenItemCollection(this);

			if(startInfo.RunHistory != null)
			{
				var historyItem = startInfo.RunHistory.GetGenItemHistoryBySourceFile(sourceFile);
				if(historyItem != null)
				{
					_LastModified = historyItem.LastModified;
				}
			}
			
		}
		readonly IGenLogger _GlobalLogger;
		private readonly IGenItemLogger _Logger;
		public IGenItemLogger Logger
		{
			get
			{
				return _Logger;
			}
		}

		ExtensionRepository _Extensions;

		public T GetExtension<T>()
		{
			return _Extensions.GetExtension<T>();
		}

		public string Name
		{
			get
			{
				return SourceFile.Name;
			}
		}

		public SourceType SourceType
		{
			get
			{
				return SourceFile.SourceType;
			}
		}

		public FileInfo File
		{
			get;
			private set;
		}

		public bool Modified
		{
			get
			{
				File.Refresh();
				if(!File.Exists)
					return true;
				return _LastModified != File.LastWriteTimeUtc;
			}
		}

		bool _Generated;
		public bool Generated
		{
			get
			{
				return _Generated;
			}
		}


		DateTime _LastModified;
		internal GenItem SetLastModified(DateTime lastModified)
		{
			_LastModified = lastModified;
			return this;
		}


		[Obsolete("Use GenItem.Children.CreateNew(string childName) instead")]
		public GenItem CreateChildItem(string childName)
		{
			return Children.CreateNew(childName);
		}
		public FileStream Open()
		{
			File.Refresh();
			if(!File.Exists)
				File.Create().Close();
			return new FileStream(File.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
		}

		public void Delete()
		{
			if(Parent != null)
				Parent.Children.Remove(this);
			else
				Repository.UnRegister(this);
		}

		

		internal void RecreateFile()
		{
			if(File.Exists)
				File.Delete();
			File.Create().Close();
			File.Refresh();
		}

		public string ReadAllText()
		{
			using(var fs = Open())
			{
				StreamReader reader = new StreamReader(fs);
				return reader.ReadToEnd();
			}
		}
		public void WriteAllText(string text)
		{
			using(var fs = Open())
			{
				StreamWriter writer = new StreamWriter(fs);
				writer.Write(text);
				writer.Flush();
			}
		}


		internal GenItemRepository Repository;


		public GenItem Parent
		{
			get;
			internal set;
		}

		private readonly GenItemCollection _Children;
		public GenItemCollection Children
		{
			get
			{
				return _Children;
			}
		}


		public override string ToString()
		{
			return SourceFile.ToString();
		}

		private readonly SourceFile _SourceFile;
		internal SourceFile SourceFile
		{
			get
			{
				return _SourceFile;
			}
		}
	}

	public class GenItemRepository : GenItemCollectionBase
	{

		Dictionary<SourceFile, GenItem> _GenItemsBySourceFiles = new Dictionary<SourceFile, GenItem>();

		private readonly ExecutionContext _ExecutionContext;
		protected override ExecutionContext ExecutionContext
		{
			get
			{
				return _ExecutionContext;
			}
		}

		internal ExecutionContext GetExecutionContext()
		{
			return _ExecutionContext;
		}
		protected override GenItem Owner
		{
			get
			{
				return null;
			}
		}

		internal GenItemRepository(ExecutionContext executionContext)
		{
			_ExecutionContext = executionContext;
		}
		protected override GenItemRepository Repository
		{
			get
			{
				return this;
			}
		}


		internal void UnRegister(GenItem child)
		{
			_GenItemsBySourceFiles.Remove(child.SourceFile);
			child.Repository = null;
			foreach(var childSource in child.Children)
				UnRegister(childSource);
		}
		internal void Register(GenItem child)
		{
			_GenItemsBySourceFiles.Add(child.SourceFile, child);
			child.Repository = this;
			foreach(var childSource in child.Children)
				Register(childSource);
		}
		internal GenItem Resolve(SourceFile sourceFile)
		{
			GenItem src = null;
			_GenItemsBySourceFiles.TryGetValue(sourceFile, out src);
			return src;
		}
		internal void RegisterAll(IEnumerable<GenItem> files)
		{
			foreach(var file in files)
			{
				Register(file);
			}
		}
		private IEnumerable<GenItem> Flatten()
		{
			var rootItems = _GenItemsBySourceFiles.Values.Where(v => v.Parent == null);
			return Flatten(rootItems);
		}

		private IEnumerable<GenItem> Flatten(IEnumerable<GenItem> items)
		{
			foreach(var item in items.ToList())
			{
				yield return item;
				foreach(var descendant in Flatten(item.Children))
				{
					yield return descendant;
				}
			}
		}

		

		#region IEnumerable<GenItem> Members

		public override IEnumerator<GenItem> GetEnumerator()
		{
			foreach(var item in Flatten().ToList())
			{
				yield return item;
			}
		}

		#endregion
	}

}
